package design.patterns.proxymode.dynamicProxy.controller;

import design.patterns.proxymode.dynamicProxy.service.IAdviceImpl;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @Description :
 * @author: cww
 * @DateTime: 2018-07-22 22:24
 */

public class JDKTheory {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.创建一个Collection的代理类 clazz类字节码,返回代理类的 java.lang.Class 对象，并向其提供类加载器和接口数组。
        System.out.println("======获得一个代理类Collection====");
        Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
        //打印查看这个代理类的名字
        System.out.println(clazzProxy1.getName());

        System.out.println("=====开始打印Collection 的constructors(构造函数) 的构造参数list========");
        //先来看看这个类的构造方法,因为有多钟构造方法所以用数组
        Constructor[] constructors = clazzProxy1.getConstructors();
        //打印出这个构造函数
        StringBuilder builder;
        for (Constructor constructor:constructors){
            //得到构造函数的名字
            String constructorName;
            constructorName = constructor.getName();
            //单线程用StringBuilder 多线程用StringBuffer
            builder = new StringBuilder(constructorName);
            builder.append('(');
            //构造函数的参数类型
            Class[] clazzParams = constructor.getParameterTypes();
            for (Class classParam:clazzParams){
                //取出参数名字
                builder.append(classParam.getName()).append(',');
            }
            if (clazzParams != null && clazzParams.length !=0){
                builder.deleteCharAt(builder.length()-1);
            }
            builder.append(')');
            System.out.println(builder);
        }

        System.out.println();
        System.out.println("=====开始打印Collection 的Methods(方法) 的构造参数list========");
        //获取这个类的所有方法
        Method[] methods = clazzProxy1.getMethods();
        //打印出这个构造函数
        StringBuilder builderForMethods;
        for (Method constructor:methods){
            //得到构造函数的名字
            String constructorName = constructor.getName();
            //单线程用StringBuilder 多线程用StringBuffer
            builderForMethods = new StringBuilder(constructorName);
            builderForMethods.append('(');
            //构造函数的参数类型
            Class[] clazzParams = constructor.getParameterTypes();
            for (Class classParam:clazzParams){
                //取出参数名字
                builderForMethods.append(classParam.getName()).append(',');
            }
            if (clazzParams != null && clazzParams.length !=0){
                builderForMethods.deleteCharAt(builderForMethods.length()-1);
            }
            builderForMethods.append(')');
            System.out.println(builderForMethods);
        }

        System.out.println();
        System.out.println("=====开始创建动态类的实例对象create instance object========");
        // 创建一个实例对象
        //从上面可以得知，clazzProxy1字节码的构造函数是带参数的，而且参数是InvocationHandler
//        clazzProxy1.newInstance();这样是取不到构造函数的因为构造函数是带参的
        //先拿到构造方法，在获得一个构造方法的时候用的是参数的类型，表示选择对象的哪个构造方法
        Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
        /**
         * 调用构造方法的时候就要使用具体的参数了，而InvocationHandler是一个接口不能直接作为参数，故要实现一下Invocation
         */
        class MyInvocationHandler1 implements InvocationHandler{

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        }
        //实现了接口得到对象后传入构造函数的参数，传入一个Handler对象(与上面一样的类型的对象)进去 通过构造函数获取对象的实例
        //返回对象
        Collection proxy1 = (Collection) constructor.newInstance(new MyInvocationHandler1());
        //打印出来为null，只是proxy1.toString()返回的是null，但是对象是有东西 的，因为没有报出空指针异常
        System.out.println(proxy1);
        //执行到clear没报错也是证明这个对象是存在的并且不是null，clear()没有返回值，没有报错
        proxy1.clear();
        //执行到size时报错了，因为size方法有返回值，报错，返回值是整形，而实际返回的是null类型不匹配
//        proxy1.size();

        //第二种写法
        Collection proxy2 = (Collection) constructor.newInstance(new InvocationHandler(){

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        });

        //第三种写法
        Collection proxy3 = (Collection) Proxy.newProxyInstance(
                Collection.class.getClassLoader(),
                new Class[]{Collection.class},
                new InvocationHandler(){
                    ArrayList list = new ArrayList();
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*******业务代码开始********/
                        System.out.println("代理执行开始，代理方法："+method.getName());
                        long beginTime = System.currentTimeMillis();
                        Thread.sleep(2000);
                        /*******业务代码结束********/
                        //调用代理的invoke，代理会将请求转发至具体实现中的目标方法
                        Object o = method.invoke(list,args);
                        /*******业务代码开始********/
                        long endTime = System.currentTimeMillis();
                        System.out.println("代理执行结束,执行时间为："+(endTime-beginTime));
                        /*******业务代码结束********/
                        return o;
                    }
                }
        );
        Object o = proxy3.add("qqq");
        System.out.println(proxy3.size());
        System.out.println(o);

        //将第三种方法进行抽取优化
        final ArrayList target = new ArrayList();
        Collection o1 =(Collection) getProxy(target,new IAdviceImpl());
        o1.add("1111");
    }
    private static Object getProxy(final Object target, final IAdviceImpl iAdvice){
        Object proxy3 =  Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler(){

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*******业务代码开始********/
                        iAdvice.beforMethod(method);
                        /*******业务代码结束********/
                        Object o = method.invoke(target,args);
                        /*******业务代码开始********/
                        iAdvice.afterMethod(method);
                        /*******业务代码结束********/
                        return o;
                    }
                }
        );
        return proxy3;
    }
}
